//
// CTVInputFilters.cpp
//

/*-----------------------------------------------------*\
			HQ Tech, Make Technology Easy!       
 More information, please go to http://hqtech.nease.net.
/*-----------------------------------------------------*/

#include "stdafx.h"
#include "CTVInputFilters.h"
#include "CVideoCaptureFilter.h"
#include "UDsUtils.h"
#include <Il21dec.h>
#include <iwstdec.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

////////////////////////////////////////////////////////////////////////
CTVInputFilters::CTVInputFilters(CDXGraph * inGraph) :
CAnalogInputFilters(inGraph)
{
	mTvTuner = NULL;
}

CTVInputFilters::~CTVInputFilters()
{
	Release();
}

BOOL CTVInputFilters::Create(void)
{
	BOOL pass = CAnalogInputFilters::Create();
	if (pass)
	{
		FindTVTuner();
	}
	return pass;
}

void CTVInputFilters::Release(void)
{
	SAFE_RELEASE(mTvTuner);
}

BOOL CTVInputFilters::FindTVTuner(void)
{
	if (mTvTuner)
	{
		return TRUE;
	}

	if (mGraphBuilder)
	{
		mGraphBuilder->FindInterface(&LOOK_UPSTREAM_ONLY,
			&MEDIATYPE_Video, mVideoCapture->GetFilter(),
			IID_IAMTVTuner, (void **)&mTvTuner);
	}

	return (mTvTuner != NULL);
}

void CTVInputFilters::ShowTVTunerPropertyPage(void)
{
	if (mTvTuner)
	{
		IBaseFilter * pFilter = NULL;
		mTvTuner->QueryInterface(IID_IBaseFilter, (void**)&pFilter);
		if (pFilter)
		{
			pFilter->Release();
			UDsUtils::ShowFilterPropertyPage(pFilter);
		}
	}
}

////////////////////////// TV related operations ///////////////////////
long CTVInputFilters::GetCountryCode(void)
{
	long  code = -1;
	if (mTvTuner)
	{
		mTvTuner->get_CountryCode(&code);
	}
	return code;
}

BOOL CTVInputFilters::SetCountryCode(long inCode)
{
	HRESULT hr = E_FAIL;
	if (mTvTuner)
	{
		hr = mTvTuner->put_CountryCode(inCode);
		if (SUCCEEDED(hr))
		{
			mTvTuner->put_TuningSpace(inCode);
		}
	}
	return SUCCEEDED(hr);
}

TunerInputType CTVInputFilters::GetTunerInputType(void)
{
	TunerInputType type = TunerInputAntenna;
	if (mTvTuner)
	{
		long  inputConnect = 0;
		if (SUCCEEDED(mTvTuner->get_ConnectInput(&inputConnect)))
		{
			mTvTuner->get_InputType(inputConnect, &type);
		}
	}
	return type;
}

BOOL CTVInputFilters::SetTunerInputType(TunerInputType inType)
{
	HRESULT hr = E_FAIL;
	if (mTvTuner)
	{
		long  inputConnect = 0;
		hr = mTvTuner->get_ConnectInput(&inputConnect);
		if (SUCCEEDED(hr))
		{
			hr = mTvTuner->put_InputType(inputConnect, inType);
		}
	}
	return SUCCEEDED(hr);
}

long CTVInputFilters::GetTVChannel(void)
{
	if (mTvTuner)
	{
		long freqIndex;
		long videoSub, audioSub;
		if (SUCCEEDED(mTvTuner->get_Channel(&freqIndex, &videoSub, &audioSub)))
		{
			return freqIndex;
		}
	}
	return 0;
}

BOOL CTVInputFilters::SetTVChannel(long inFrequencyIndex)
{
	if (mTvTuner)
	{
		long signalStrength  = AMTUNER_NOSIGNAL;
		long videoSubChannel = AMTUNER_SUBCHAN_DEFAULT;
		long audioSubChannel = AMTUNER_SUBCHAN_DEFAULT;
		mTvTuner->put_Channel(inFrequencyIndex, videoSubChannel, audioSubChannel);
		mTvTuner->SignalPresent(&signalStrength);
		if (signalStrength != AMTUNER_NOSIGNAL)
		{
			return TRUE;
		}
	}
	return FALSE;
}

BOOL CTVInputFilters::GetChannelMinMax(long * outMin, long * outMax)
{
	if (mTvTuner)
	{
		if (SUCCEEDED(mTvTuner->ChannelMinMax(outMin, outMax)))
		{
			return TRUE;
		}
	}
	return FALSE;
}

Signal_Resolution CTVInputFilters::GetTunerResolution(void)
{
	if (mTvTuner)
	{
		long resolution = 0;
		mTvTuner->get_TVFormat(&resolution);

		if (cNTSCMaskBits & resolution)
		{
			return SR_NTSC;
		}
		else if (cPALMaskBits & resolution)
		{
			return SR_PAL;
		}
		else if (cSECAMMaskBits & resolution)
		{
			return SR_SECAM;
		}
	}
	
	return SR_UNKNOWN;
}

// Demo: how to auto-tune TV channels...
void CTVInputFilters::DemoTVChannelAutoTune(void)
{
	if (mTvTuner)
	{
		long minChannel = 0, maxChannel = 0;
		long foundSignal = AMTUNER_NOSIGNAL;

		HRESULT hr = mTvTuner->ChannelMinMax(&minChannel, &maxChannel);
		for (long i = minChannel; i <= maxChannel; i++)
		{
			hr = mTvTuner->AutoTune(i, &foundSignal);

			// Check the signal strength
			// Assume AMTUNER_HASNOSIGNALSTRENGTH means tuned
			if (foundSignal != AMTUNER_NOSIGNAL)
			{
			}
		}

		// Save the fine-tuning information for all channels to the registry
		if (SUCCEEDED(hr))
		{
			mTvTuner->StoreAutoTune(); 
		}
	}
}

////////////////////////// VBI related operations ////////////////////////
// Render the closed captioning pin? It could be a CC or a VBI category pin,
// depending on the capture driver
BOOL CTVInputFilters::TryCCFilters(void)
{
	if (!mGraphBuilder)
	{
		return FALSE;
	}

	HRESULT hr = mGraphBuilder->RenderStream(&PIN_CATEGORY_CC, 0,
		mVideoCapture->GetFilter(), 0, 0);
	if (hr != NOERROR)
	{
		hr = mGraphBuilder->RenderStream(&PIN_CATEGORY_VBI, 0,
			mVideoCapture->GetFilter(), 0, 0);
	}

	return SUCCEEDED(hr);
}

BOOL CTVInputFilters::DisplayClosedCaption(BOOL inIsOn)
{
	if (!mGraphBuilder)
	{
		return FALSE;
	}

	IAMLine21Decoder *pLine21 = NULL;
	HRESULT hr = mGraphBuilder->FindInterface(
		&LOOK_DOWNSTREAM_ONLY,        // Look downstream from pCap 
		NULL,                         // No particular media type
		mVideoCapture->GetFilter(),   // Pointer to the capture filter.
		IID_IAMLine21Decoder, 
		(void**)&pLine21
		);
	if (SUCCEEDED(hr))
	{
		pLine21->SetServiceState(inIsOn ? AM_L21_CCSTATE_On : AM_L21_CCSTATE_Off);
		pLine21->Release();
	}

	return SUCCEEDED(hr);
}

// Support WST
BOOL CTVInputFilters::TryWSTFilters(void)
{
	if (!mGraphBuilder)
	{
		return FALSE;
	}

	IBaseFilter * pKernelTee = NULL;
	IBaseFilter * pWstCodec  = NULL;
	HRESULT hr = NOERROR;
	BOOL  pass = FALSE;

	pKernelTee = UDsUtils::CreateHardwareFilter(AM_KSCATEGORY_SPLITTER, "Tee/Sink-to-Sink Converter");
	pass = (pKernelTee != 0);
	if (pass)
	{
		mGraph->GetGraph()->AddFilter(pKernelTee, L"Kernel Tee");
		pWstCodec  = UDsUtils::CreateHardwareFilter(AM_KSCATEGORY_VBICODEC, "WST Codec");
		pass = (pWstCodec != 0);
	}
	if (pass)
	{
		mGraph->GetGraph()->AddFilter(pWstCodec, L"WST Codec");
		hr = mGraphBuilder->RenderStream(&PIN_CATEGORY_VBI, 0, mVideoCapture->GetFilter(), 
			pKernelTee, pWstCodec);
		pass = SUCCEEDED(hr);
	}
	if (pass)
	{
		hr = mGraphBuilder->RenderStream(0, 0, pWstCodec, 0, 0);
		pass = SUCCEEDED(hr);
	}
	if (pass)
	{
		Verify_VPVBI_Connection();
	}

	if (pKernelTee)
	{
		pKernelTee->Release();
		pKernelTee = 0;
	}
	if (pWstCodec)
	{
		pWstCodec->Release();
		pWstCodec = 0;
	}
	
	return pass;
}

// If the capture filter uses VPVBI pin, make sure that this pin has been
// connect to "VBI Surface Allocator" filter before running.
void CTVInputFilters::Verify_VPVBI_Connection(void)
{
	if (mGraphBuilder)
	{
		IPin * pVPVBI = NULL;
		HRESULT hr = mGraphBuilder->FindPin(
			mVideoCapture->GetFilter(), 
			PINDIR_OUTPUT,
			&PIN_CATEGORY_VIDEOPORT_VBI, 
			NULL, 
			TRUE, 
			0, 
			&pVPVBI
			);
		if (hr == S_OK)
		{
			// If found, connect it to the VBI Surface Allocator.
			// Create VBI Surface Allocator filter, and add it to the filter graph.
			IBaseFilter * pSurf = NULL;
			hr = CoCreateInstance(CLSID_VBISurfaces, NULL, CLSCTX_INPROC_SERVER, 
				IID_IBaseFilter, (void **)&pSurf);
			if (SUCCEEDED(hr))
			{
				hr = mGraph->GetGraph()->AddFilter(pSurf, L"VBI Surf");
				hr = mGraphBuilder->RenderStream(NULL, NULL, pVPVBI, 0, pSurf);
				pSurf->Release();
			}
			pVPVBI->Release();
		}
	}
}

// Control the displaying status of World Standard Teletext
BOOL CTVInputFilters::DisplayWSTText(BOOL inIsOn)
{
	if (!mGraphBuilder)
	{
		return FALSE;
	}

	IAMWstDecoder *pwst = NULL;
	HRESULT hr = mGraphBuilder->FindInterface(
		&LOOK_DOWNSTREAM_ONLY,        // Look downstream from pCap 
		NULL,                         // No particular media type
		mVideoCapture->GetFilter(),   // Pointer to the capture filter.
		IID_IAMWstDecoder, 
		(void**)&pwst
		);
	if (SUCCEEDED(hr))
	{
		pwst->SetServiceState(inIsOn ? AM_WST_STATE_On : AM_WST_STATE_Off);
		pwst->Release();
	}
	return SUCCEEDED(hr);
}
